package com.eight.cloud.message.config;

import com.eight.cloud.message.handler.WebsocketHandshakeHandler;
import com.eight.cloud.message.interceptor.ServletWebSocketHandshakeInterceptor;
import com.eight.cloud.message.interceptor.WebSocketChannelInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler;
import org.springframework.web.socket.config.annotation.*;

import javax.annotation.Resource;
import java.util.List;

import static com.eight.cloud.openfeign.message.constant.SocketConstant.*;

/**
 * websocket配置
 *
 * @ClassName: ServletWebSocketServerConfigurer
 * @Author: TXC
 * @Date: 2024-10-27 15:25
 **/
@Configuration
@EnableWebSocketMessageBroker
@Slf4j
public class ServletWebSocketServerConfigurer implements WebSocketMessageBrokerConfigurer {
    @Resource
    private ServletWebSocketHandshakeInterceptor webSocketHandshakeInterceptor;
    @Resource
    private WebSocketChannelInterceptor webSocketChannelInterceptor;
    @Resource
    private WebsocketHandshakeHandler websocketHandshakeHandler;

    /**
     * 添加一个Stomp协议的Endpoint，这样在网页中就可以通过websocket连接上服务,也就是我们配置websocket的服务地址,并且可以指定是否使用socketJs
     * @param registry StompEndpointRegistry
     */
    @Override
    public void registerStompEndpoints(@NotNull StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*")
                .setHandshakeHandler(websocketHandshakeHandler)
                .addInterceptors(webSocketHandshakeInterceptor);
    }

    /**
     * 配置消息代理，哪种路径的消息会进行代理处理
     * @param registry {@link MessageBrokerRegistry}
     */
    @Override
    public void configureMessageBroker(@NotNull MessageBrokerRegistry registry) {
        /*
         * spring 内置broker对象
         * 1. 配置代理域，可以配置多个，此段代码配置代理目的地的前缀为 /topicTest 或者 /userTest
         *    我们就可以在配置的域上向客户端推送消息
         * 2，进行心跳设置，第一值表示server最小能保证发的心跳间隔毫秒数, 第二个值代码server希望client发的心跳间隔毫秒数
         * 3. 可以配置心跳线程调度器 setHeartbeatValue这个不能单独设置，不然不起作用，要配合setTaskScheduler才可以生效
         *    调度器我们可以自己写一个，也可以自己使用默认的调度器 new DefaultManagedTaskScheduler()
         */
        registry.enableSimpleBroker(SOCKET_TOPIC, SOCKET_QUEUE, SOCKET_MASSAGE)
                .setHeartbeatValue(new long[]{10000,10000})
                .setTaskScheduler(new DefaultManagedTaskScheduler());
        /*
         *  "/app" 为配置应用服务器的地址前缀，表示所有以/app 开头的客户端消息或请求都会路由到带有@MessageMapping 注解的方法中
         */
        registry.setApplicationDestinationPrefixes(SOCKET_MASSAGE);
        /*
         *  1. 配置一对一消息前缀， 客户端接收一对一消息需要配置的前缀 如“'/user/'+userid + '/message'”，
         *     是客户端订阅一对一消息的地址 stompClient.subscribe js方法调用的地址
         *  2. 使用@SendToUser发送私信的规则不是这个参数设定，在框架内部是用UserDestinationMessageHandler处理，
         *     而不是 AnnotationMethodMessageHandler 或  SimpleBrokerMessageHandler
         *     or StompBrokerRelayMessageHandler，是在@SendToUser的URL前加“user+sessionId"组成
         */
        registry.setUserDestinationPrefix(SOCKET_USER);
    }

    /**
     * 配置发送与接收的消息参数，可以指定消息字节大小，缓存大小，发送超时时间
     * @param registry {@link WebSocketTransportRegistration}
     */
    @Override
    public void configureWebSocketTransport(@NotNull WebSocketTransportRegistration registry) {
        /*
         * 1. setMessageSizeLimit 设置消息缓存的字节数大小 字节
         * 2. setSendBufferSizeLimit 设置websocket会话时，缓存的大小 字节
         * 3. setSendTimeLimit 设置消息发送会话超时时间，毫秒
         */
        registry.setMessageSizeLimit(10240)
                .setSendBufferSizeLimit(10240)
                .setSendTimeLimit(10000);
    }

    /**
     * 设置输入消息通道的线程，默认线程为1，可以自己自定义线程数，最大线程数，线程存活时间
     * @param registration {@link ChannelRegistration}
     */
    @Override
    public void configureClientInboundChannel(@NotNull ChannelRegistration registration) {
        /*
         * 配置消息线程池
         * 1. corePoolSize 配置核心线程池，当线程数小于此配置时，不管线程中有无空闲的线程，都会产生新线程处理任务
         * 2. maxPoolSize 配置线程池最大数，当线程池数等于此配置时，不会产生新线程
         * 3. keepAliveSeconds 线程池维护线程所允许的空闲时间，单位秒
         */
        registration.taskExecutor().corePoolSize(10)
                .maxPoolSize(20)
                .keepAliveSeconds(60);
        /*
         * 添加stomp自定义拦截器，可以根据业务做一些处理
         * 消息拦截器，实现ChannelInterceptor接口
         */
        registration.interceptors(webSocketChannelInterceptor);
    }

    /**
     * 设置输出消息通道的线程，默认线程为1，可以自己自定义线程数，最大线程数，线程存活时间
     * @param registration {@link ChannelRegistration}
     */
    @Override
    public void configureClientOutboundChannel(@NotNull ChannelRegistration registration) {
        registration.taskExecutor().corePoolSize(10)
                .maxPoolSize(20)
                .keepAliveSeconds(60);
        registration.interceptors(webSocketChannelInterceptor);
    }

    /**
     * 自定义控制器方法的参数类型
     * @param argumentResolvers {@link List < HandlerMethodArgumentResolver >}
     */
    @Override
    public void addArgumentResolvers(@NotNull List<HandlerMethodArgumentResolver> argumentResolvers) {
        WebSocketMessageBrokerConfigurer.super.addArgumentResolvers(argumentResolvers);
    }

    /**
     * 自定义控制器方法返回值类型
     * @param returnValueHandlers {@link List<HandlerMethodReturnValueHandler>}
     */
    @Override
    public void addReturnValueHandlers(@NotNull List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        WebSocketMessageBrokerConfigurer.super.addReturnValueHandlers(returnValueHandlers);
    }

    /**
     * 添加自定义的消息转换器，spring 提供多种默认的消息转换器，返回false,不会添加消息转换器，
     * 返回true，会添加默认的消息转换器，当然也可以把自己写的消息转换器添加到转换链中
     * @param messageConverters {@link List<MessageConverter>}
     * @return boolean 是否添加自定义的消息转换器
     */
    @Override
    public boolean configureMessageConverters(@NotNull List<MessageConverter> messageConverters) {
        return WebSocketMessageBrokerConfigurer.super.configureMessageConverters(messageConverters);
    }
}
